#include "uartdevice.h"
#include "iot_debug.h"

#include <termios.h>
#include <fcntl.h>
#include <logger.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/serial_core.h>
#include <asm-generic/ioctl.h>
#include <time.h>

static uint32_t convertBaudrate(uint32_t baudrate)
{
    switch (baudrate) {
    case 115200:    return B115200;
    case 57600:     return B57600;
    case 38400:     return B38400;
    case 19200:     return B19200;
    case 9600:      return B9600;
    case 4800:      return B4800;
    case 2400:      return B2400;
    case 1200:      return B1200;
    case 600:       return B600;
    case 300:       return B300;
    case 200:       return B200;
    case 150:       return B150;
    case 110:       return B110;
    case 230400:    return B230400;
    case 460800:    return B460800;
    case 500000:    return B500000;
    case 576000:    return B576000;
    case 921600:    return B921600;
    case 1000000:   return B1000000;
    case 1152000:   return B1152000;
    case 1500000:   return B1500000;
    case 2000000:   return B2000000;
    case 2500000:   return B2500000;
    default:        return B9600;
    }
}

static int InitUart(const char *dev_name, uint32_t baudrate,
                    uint8_t data_bits, uint8_t stop_bits,
                    char parity)
{
    int fd = -1;
    bool rc = false;
    struct termios options, options_old;
    baudrate = convertBaudrate(baudrate);

    if(strstr(dev_name, ":36072"))
    {
        fd = 0;
        rc = true;
    }
    else
    {
        if (strcmp(dev_name, "") == 0)
        {
            log_warn("open uart %s error!\n", dev_name);
            goto error;
        }
        fd = ::open(dev_name, O_RDWR | O_NOCTTY | O_NDELAY);
        if(fd == -1) {
            goto error;
        }
        if(tcgetattr(fd, &options_old) != 0)
            goto error;
        memset(&options, 0, sizeof(struct termios));
        if((cfsetispeed(&options, baudrate) < 0) ||
           (cfsetospeed(&options, baudrate) < 0))
            goto error;
        options.c_cflag |= (CREAD | CLOCAL);
        options.c_cflag &= ~CSIZE;

        switch (data_bits) {
        case 7:
            options.c_cflag |= CS7;
            break;
        default:
            options.c_cflag |= CS8;
            break;
        }
        switch (stop_bits) {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            options.c_cflag &= ~CSTOPB;
            break;
        }

        parity = static_cast<char>(toupper(parity));
        switch (parity) {
        case 'N':
            options.c_cflag &= ~PARENB;
            break;
        case 'O':
            options.c_cflag |= (PARODD | PARENB);
         break;
        case 'E':
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
         break;
        case 'S':
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
         break;
        default:
            log_error("Unsupported parity(%c) %s\n", parity, dev_name);
            goto error;
        }
        if (parity == 'N')
            options.c_iflag &= ~INPCK;
        else
            options.c_iflag |= INPCK;
        options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        options.c_oflag &=~ OPOST;
        options.c_cc[VTIME] = 0;
        options.c_cc[VMIN]  = 0;

        if(tcsetattr(fd, TCSANOW, &options) != 0)
        {
            goto error;
        }
        //清除全部缓冲数据
        tcflush(fd, TCIOFLUSH);

        log_info("open uart %s suceess! \n", dev_name);
        rc = true;
    }

error:
    if (!rc) {
        if (fd) {
            ::close(fd);
            fd = -1;
        }
    }
    return fd;
}

UartDevice::UartDevice(const char *dev_name, uint32_t baudrate, char parity, bool open_daly)
:   open_daly_(open_daly)
,   dev_name_(dev_name)
,   baudrate_(baudrate)
,   parity_(parity)
{
    if (open_daly_)
    {
        fd_ = -1;
        return;
    }
    else
    {
        openUart();
    }
}

UartDevice::~UartDevice()
{

    if (fd_ != -1)
    {
        ::close(fd_);
        fd_ = -1;
    }
}

bool UartDevice::isActived() const
{
    return (fd_ != -1) ? true : false;
}

bool UartDevice::SwitchSettings(ComSettings *settings, bool isReopen)
{
    dev_name_ = settings->dev_name;
    baudrate_ = settings->uart.baudrate;
    parity_   = settings->uart.parity;
    if (!isReopen)
    {
        return true;
    }

    bool rc = false;
    if(strstr(dev_name_.c_str(), ":36072"))
    {
        rc = true;
    }

    if (!rc)
    {
        if (fd_)
        {
            ::close(fd_);
            fd_ = -1;
        }
    }

    return true;
}

void UartDevice::openUart()
{
    fd_ = InitUart(dev_name_.c_str(), baudrate_, 8, 1, parity_);
    if (fd_ > 0)
    {
        log_info("Init Uart %s success! baudrate:%d\n", dev_name_.c_str(), baudrate_);
    }
}

bool UartDevice::connect(int /*timeout*/)
{
    if (open_daly_ && fd_ < 0)
    {
        openUart();
    }

    return (fd_ != -1) ? true : false;
}

void UartDevice::disconnect()
{

}

bool UartDevice::tryLock()
{
    return mtx_.trylock();
}

void UartDevice::lock()
{
    mtx_.lock();
}

void UartDevice::unlock()
{
    mtx_.unlock();
}

uint32_t UartDevice::read(void *buf, uint32_t max_byte)
{
    if (open_daly_ && fd_ < 0)
    {
        openUart();
    }
    if (fd_ < 0)
    {
        return 0;
    }
    int read_size = 0;
    uint8_t wait_cnt = 0;
    uint8_t *rx_data = reinterpret_cast<uint8_t *>(buf);

    read_size = ::read(fd_, buf, max_byte);
    if (read_size < 0)
    {
        read_size = 0;
        if (errno != EAGAIN)
        {
            log_error("Uart %s Read failed due to %d\n", dev_name_.c_str(), errno);
            ::close(fd_);
            fd_ = -1;
        }
    }
    return read_size;
}


uint32_t UartDevice::read(void *buf, uint32_t max_byte, uint32_t timeout)
{
    if (open_daly_ && fd_ < 0)
    {
        openUart();
    }
    if (fd_ == -1)
    {
        return 0;
    }
    int read_size = 0;
    uint32_t n = 0;
    uint8_t wait_cnt = 0;
    uint8_t *rx_data = reinterpret_cast<uint8_t *>(buf);
    uint32_t wait_time = timeout*50;

    do {
        read_size = read(rx_data+n, max_byte-n);
        if (read_size > 0) {
            wait_cnt = 0;
            wait_time = settings_->recv_char_timeout_ms*50;
            n += static_cast<uint32_t>(read_size);
        }
        if (n == max_byte)
            break;
        else {
            wait_cnt++;
            if (wait_cnt < 20) {
                uSleep(wait_time);
                continue;
            }
            break;
        }
    } while(true);

    return n;
}

int UartDevice::async_read(uint8_t *RecvBuf, uint32_t max_byte, uint32_t i_RecvTimeoutS)
{
    if (open_daly_ && fd_ < 0)
    {
        openUart();
    }
    if (fd_ == -1)
    {
        return -2;
    }

    unsigned char cTimeoutCount,cRecvFlag,cStopRecv;
    int iRetVal,iRetVal2,iResult,iResult2;
    int iRecvBytes,iRecvBytes2,iTotalBytes;

    struct timeval tv_timeout;
    fd_set fs_read;

    unsigned char *m_cpRecvBuf = NULL;
    unsigned char m_cRecvBuf[512];

    unsigned int realMaxSize = (max_byte > sizeof(m_cRecvBuf)) ? (sizeof(m_cRecvBuf)) : (max_byte);
    realMaxSize = (max_byte < sizeof(m_cRecvBuf)) ? (max_byte) : (sizeof(m_cRecvBuf));

    memset(m_cRecvBuf, 0x0, sizeof(m_cRecvBuf));
    m_cpRecvBuf = m_cRecvBuf;
    cTimeoutCount = cRecvFlag = 0x0;
    cStopRecv = 0x0;

    while(1)
    {
        tv_timeout.tv_sec = 0;
        tv_timeout.tv_usec  = 500000;
        FD_ZERO(&fs_read);
        FD_SET(fd_, &fs_read);

        iRetVal = iResult = -1;
        iRecvBytes = iTotalBytes = 0;
        iRetVal = select(fd_ + 1, &fs_read, NULL, NULL, &tv_timeout);
        if (iRetVal>0)
        {
            iRecvBytes = m_cpRecvBuf - m_cRecvBuf;
            iResult = read(m_cpRecvBuf, realMaxSize-iRecvBytes);
            if (iResult<=0){
                cTimeoutCount++;
                if (cTimeoutCount >= i_RecvTimeoutS) break;
                continue;
            }
            else if (iResult>0){

                if ( (iResult==1) && (m_cpRecvBuf[0]==0x68) ){
                    continue;
                }

                iRetVal2 = iResult2 = -1;
                iRecvBytes2 = 0;
                cRecvFlag = 0x5a;
                cTimeoutCount = 0;

                m_cpRecvBuf = m_cpRecvBuf + iResult;
                iTotalBytes = iRecvBytes + iResult;
                if (m_cpRecvBuf >= (m_cRecvBuf+realMaxSize)){
                    m_cpRecvBuf = m_cRecvBuf;
                    iTotalBytes = realMaxSize;
                    cStopRecv = 0x5a;
                }
                else{
                    while(1)
                    {
                        tv_timeout.tv_sec = 0;
                        tv_timeout.tv_usec = 500 * 1000;
                        FD_ZERO(&fs_read);
                        FD_SET(fd_, &fs_read);

                        iRetVal2 = select((int)fd_ + 1, &fs_read, NULL, NULL, &tv_timeout);
                        if (iRetVal2 > 0){
                            iRecvBytes2 = m_cpRecvBuf - m_cRecvBuf;
                            iResult2 = read(m_cpRecvBuf, realMaxSize-iRecvBytes2);
                            if (iResult2 < 0){
                                cStopRecv = 0x5a;
                                break;
                            }
                            m_cpRecvBuf = m_cpRecvBuf + iResult2;
                            iTotalBytes = iRecvBytes2 + iResult2;
                            if (m_cpRecvBuf>=(m_cRecvBuf+realMaxSize)){
                                m_cpRecvBuf = m_cRecvBuf;
                                iTotalBytes = realMaxSize;
                                cStopRecv = 0x5a;
                                break;
                            }
                        }
                        else if(iRetVal2<=0){
                            cStopRecv = 0x5a;
                            break;
                        }
                    }
                }
                if (iTotalBytes > (int)realMaxSize){
                    iTotalBytes = (int)realMaxSize;
                }
                if (cStopRecv==0x5a) break;
            }
        }
        else if(iRetVal <= 0){
            cTimeoutCount++;
            if (cTimeoutCount >= i_RecvTimeoutS) break;
        }
    }

    if (cRecvFlag != 0x5a){
        return -1;
    }

    memcpy(RecvBuf, m_cRecvBuf, iTotalBytes);
    return iTotalBytes;
}

uint32_t UartDevice::write(const void *buf, uint32_t max_byte)
{
    if (open_daly_ && fd_ < 0)
    {
        openUart();
    }
    if (fd_ < 0)
        return 0;
    int write_count = ::write(fd_, buf, max_byte);
    if (write_count < 0)
    {
        write_count = 0;
        log_error("Uart %s Write failed due to %d\n", dev_name_.c_str(), errno);
        ::close(fd_);
        fd_ = -1;
    }

    return write_count;
}

void UartDevice::clear()
{
    if (open_daly_ && fd_ < 0)
    {
        openUart();
    }

    tcflush(fd_, TCIOFLUSH);
}

void UartDevice::restore()
{
    if (fd_ >= 0)
    {
        ::close(fd_);
        fd_ = -1;
        open_daly_ = true;
        log_warn("Restore Uart %s\n", dev_name_.c_str());
    }
}
